package cn.sh.ideal.yedis.core;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import cn.sh.ideal.yedis.util.Serializer;
import redis.clients.jedis.BinaryJedisPubSub;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.util.SafeEncoder;

public class AbstractOperation<V> {
	RedisTemplate<V> template;
	
	static Map<Subcriber,BinaryJedisPubSub> listeners = new ConcurrentHashMap<>();
	
	AbstractOperation(RedisTemplate<V> template) {
		this.template = template;
	}
	/**
	 * 序列化
	 * @param object
	 * @return
	 */
	public byte[] raw(Object object) {
		if(null == object) return null;
		return Serializer.serialize(object);
	}
	
	/**
	 * 反序列化
	 * @param source
	 * @return
	 */
	public <T> T convert(byte[] source) {
		if(source == null) return null;
		
		return Serializer.deserialize(source);
	}
	
	/**
	 * 序列化key
	 * @param key
	 * @return
	 */
	public byte[] rawKey(String key) {
		return SafeEncoder.encode(key);
	}
	
	/**
	 * 将字符串数组转换成byte[]数组
	 * @param keys
	 * @return
	 */
	public byte[][] toByteArray(String[] keys){
		byte[][] byteArray = new byte[keys.length][];
		for(int i =0; i < keys.length; i++) {
			byteArray[i] = rawKey(keys[i]);
		}
		
		return byteArray;
	}
	
	/**
	 * 将字符串数组转换成byte[]数组
	 * @param keys
	 * @return
	 */
	public byte[][] toByteArray(V[] values){
		byte[][] byteArray = new byte[values.length][];
		for(int i =0; i < values.length; i++) {
			byteArray[i] = raw(values[i]);
		}
		
		return byteArray;
	}
	
	/**
	 * 将集合转换成byte[]数组
	 * @param keys
	 * @return
	 */
	public byte[][] toByteArray(List<V> values){
		int size = values.size();
		byte[][] byteArray = new byte[size][];
		for(int i =0; i < size; i++) {
			byteArray[i] = raw(values.get(i));
		}
		
		return byteArray;
	}
	
	/**
	 * byte[]列表转对象列表
	 * @param bytelist
	 * @return
	 */
	public List<V> toObjectList(Collection<byte[]> bytelist){
		List<V> datas = new ArrayList<>();
		if(null != bytelist && !bytelist.isEmpty()) {
			for(byte[] bdata : bytelist) {
				V data = convert(bdata);
				datas.add(data);
			}
		}
		
		return datas;
	}
	
	
	/**
	 * byte[]列表转对象列表-批量操作时用
	 * @param bytelist
	 * @return
	 */
	public List<V> bytetoObjectList(List<Object> objlist){
		List<V> datas = new ArrayList<>();
		if(null != objlist && !objlist.isEmpty()) {
			for(Object obj : objlist) {
				V data = convert((byte[])obj);
				datas.add(data);
			}
		}
		
		return datas;
	}
	
	/**
	 * byte[]列表转对象列表
	 * @param bytelist
	 * @return
	 */
	public List<V> toObjectList(Set<byte[]> byteset){
		List<V> datas = new ArrayList<>();
		if(null != byteset && !byteset.isEmpty()) {
			for(byte[] bdata : byteset) {
				V data = convert(bdata);
				datas.add(data);
			}
		}
		
		return datas;
	}
	
	/**
	 * byte[]集合转对象集合
	 * @param byteset
	 * @return
	 */
	public Set<V> toObjectSet(Set<byte[]> byteset){
		Set<V> datas = new HashSet<>();
		if(byteset != null && !byteset.isEmpty()) {
			for(byte[] bdata : byteset) {
				V data = convert(bdata);
				datas.add(data);
			}
		}
		
		return datas;
	}
	
	/**
	 * 对象map转byte[] map
	 * @param hash
	 * @return
	 */
	public Map<byte[],byte[]> toByteMap(Map<String,V> hash){
		Map<byte[],byte[]> dataMap = new HashMap<>();
		for(String field : hash.keySet()) {
			V value = hash.get(field);
			if(null == value) continue;
			dataMap.put(rawKey(field), raw(value));
		}
		
		return dataMap;
	}
	
	/**
	 * 对象map转byte[] map
	 * @param hash
	 * @return
	 */
	public Map<String,V> toObjectMap(Map<byte[],byte[]> hash){
		Map<String,V> dataMap = new HashMap<>();
		if(null != hash && !hash.isEmpty()) {
			for(byte[] filed : hash.keySet()) {
				V value = convert(hash.get(filed));
				dataMap.put(decode(filed), value);
			}
		}
		
		return dataMap;
	}
	
	/**
	 * 字符串解码
	 * @param b
	 * @return
	 */
	public String decode(byte[] b) {
		String str = null;
		if(b != null) {
			try {
				str = new String(b, Protocol.CHARSET);
			} catch (UnsupportedEncodingException e) {
				throw new JedisException(e);
			}
		}
		
		return str;
	}
	
	public <T> T execute(RedisCallback<Jedis,T> callback) {
		return template.execute(callback);
	}
	
	public <T> T executeCluster(RedisCallback<JedisCluster,T> callback) {
		return template.executeCluster(callback);
	}
	
	public BinaryJedisPubSub getListener(final String channel,final Subcriber<V> subcriber) {
		BinaryJedisPubSub listener = new BinaryJedisPubSub() {
			public void onMessage(byte[] channel, byte[] message) {
				String ch = decode(channel);
				V objMessage = null;
				if(ch.equals("__keyevent@0__:expired")) {
					objMessage = (V)new String(message); 
				}else {
					objMessage = convert(message);
				}
				
				subcriber.onMessage(decode(channel), objMessage);
			}
			public void onSubscribe(byte[] channel, int subscribedChannels) {
				subcriber.onSubscribe(decode(channel), subscribedChannels);
			}

			public void onUnsubscribe(byte[] channel, int subscribedChannels) {
				subcriber.onUnsubscribe(decode(channel), subscribedChannels);
			}
		};
		
		listeners.put(subcriber, listener);
		
		return listener;
	}
	
	public void unsubcribe(Subcriber<V> subcriber) {
		BinaryJedisPubSub listener = listeners.get(subcriber);
		try {
			if(listener == null) {
				Thread.currentThread().sleep(1000);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		listener = listeners.get(subcriber);
		if(null != listener) {
			listener.unsubscribe();
			listeners.remove(subcriber);
		}
	}
}
